﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

namespace MyAssetBundles {
    public class MyAssetBundlesLauncher : CustomYieldInstruction {
        public string error = null;
        public bool isDone = false;
        public override bool keepWaiting
        {
            get {
                return !isDone;
            }
        }

        public static MyAssetBundlesLauncher Create<T> (string name) where T : MyAssetBundlesMono
        {
            return new GameObject(name).AddComponent<T>().launcher;
        }
    }

    public abstract class MyAssetBundlesMono : MonoBehaviour {
        public MyAssetBundlesLauncher launcher;
        protected List<string> _assetBundlesNameList;
        public abstract MyAssetBundlesUpdateMode updateMode { get; }
        public abstract string serverUrl { get; }
        public abstract string localPath { get; }
        public abstract string versionsName { get; }
        public string packageSavePath = Path.GetDirectoryName(Path.GetTempPath());
        public ulong decompressCodeOneBlockSpeed = 1024 * 126;
        public abstract IEnumerator Init ();
        public abstract IEnumerator Complete ();
        public abstract void Clean ();
        void Awake ()
        {
            launcher = new MyAssetBundlesLauncher();
            _assetBundlesNameList = new List<string>();
        }

        IEnumerator Start ()
        {
            DontDestroyOnLoad(gameObject);
            yield return Run();
        }

        IEnumerator Run ()
        {
            yield return Init();
            yield return LoadAssetBundlesVersions();
            yield return new WaitForEndOfFrame();
            yield return Complete();
            Destroy(this.gameObject);
        }

        IEnumerator LoadAssetBundlesVersions ()
        {
            if (serverUrl == null || localPath == null || versionsName == null)
                yield break;

            // test
            packageSavePath = Directory.CreateDirectory(localPath).Parent.ToString();
            
            // 服务版本信息
            MyAssetBundlesVersions versionsSer = new MyAssetBundlesVersions(packageSavePath);
            yield return versionsSer.LoadWebPath(MyAssetBundlesUtil.PathCombine(serverUrl, versionsName));
            Debug.Log("asset bundles package save temp path: " + packageSavePath);

            //本地版本信息
            MyAssetBundlesVersions versionsApp = new MyAssetBundlesVersions("");
            versionsApp.LoadLocalPath(MyAssetBundlesUtil.PathCombine(localPath, versionsName));
            
            foreach (var operation in versionsSer)
            {
                if (operation.updateMode != updateMode) continue;
                if (versionsApp.GetInfoWithVersion(operation.version, updateMode) != null)
                    continue;

                yield return RunAssetBundlesWithVersions(operation);
                versionsApp.AddVersion(operation);
            }
            if (_assetBundlesNameList.Count > 0)
                _assetBundlesNameList = new List<string>(new HashSet<string>(_assetBundlesNameList));

        }

        IEnumerator RunAssetBundlesWithVersions (MyVersionOperation operation)
        {
            Debug.Log("update version : " + operation.version);
            DownloadFolder dFolder = GetAssetBundles<DownloadFolder>();
            dFolder.downloadByteTotal = operation.compressSize;
            dFolder.downloadFileName = operation.compressPackage;
            yield return dFolder.GenerateEnumerator(serverUrl + Path.GetFileName(operation.compressPackage), FolderProgress.Create(DownloadProgress));
            yield return new WaitForEndOfFrame();

            DecompressFolder decompress = GetAssetBundles<DecompressFolder>();
            decompress._inPath = operation.compressPackage;
            decompress._inTotalSize = operation.compressSize;
            decompress._outTotalSize = operation.decompressSize;
            decompress._codeOneBlockSpeed = decompressCodeOneBlockSpeed;
            yield return decompress.GenerateEnumerator(operation.decompressPackage, FolderProgress.Create(DecompressProgress));

            Debug.Log("analysis start");
            AnalysisFolder analysis = GetAssetBundles<AnalysisFolder>();
            analysis.Init(updateMode);
            analysis.Generate(operation.decompressPackage, null);
            yield return new WaitForEndOfFrame();

            Debug.Log("unpack start");
            UnpackFolder unpack = GetAssetBundles<UnpackFolder>(analysis);
            unpack.Generate(localPath, FolderProgress.Create(UnpackProgress));
            _assetBundlesNameList.AddRange(unpack.assetBundlesNameList);
            yield return new WaitForEndOfFrame();
            
            File.Delete(dFolder.downloadFileName);
            ResetAsssetBundles();
        }

        public virtual void DownloadProgress (ProgressTarget target)
        {

        }

        public virtual void DecompressProgress (ProgressTarget target)
        {

        }

        public virtual void UnpackProgress (ProgressTarget target)
        {

        }

        protected T GetAssetBundles<T> (BaseFolder target = null) where T : BaseFolder
        {
            T t = gameObject.AddComponent<T>();
            if (target != null)
                t.Init(target);
            return t;
        }

        void ResetAsssetBundles ()
        {
            Component[] arr = gameObject.GetComponents<BaseFolder>();
            foreach (var target in arr)
            {
                DestroyImmediate(target);
            }
        }

        void OnDestroy ()
        {
            launcher.isDone = true;
            Clean();
        }
    }
}

